/**
 * 
 */
package ch.unisi.inf.pfii.teamred.pacman.model;

import java.util.ArrayList;
import java.util.HashMap;

import ch.unisi.inf.pfii.teamred.pacman.controller.CreatureListener;

/**
 * This class describes an abstract creature that can move in the game (i.e.
 * Pacman, Ghost).
 * 
 * 
 * @author mark.pruneri@lu.unisi.ch
 * @author luca.vignola@lu.unisi.ch
 */
public abstract class Creature {

	private final Position startPosition;
	private final ArrayList<CreatureListener> listeners;
	private final int points;

	private HashMap<Direction, Position> directionToPosition;
	private Position currentPosition;
	private Direction currentDirection;
	private ArrayList<Item> currentItems;
	private ArrayList<Creature> creaturesOnCurrentFloor;
	private String name;
	private boolean dead;
	private boolean vulnerable;
	private int timer;

	public Creature(final Position startPosition,
			final Direction currentDirection, final int points) {
		this.startPosition = startPosition;
		listeners = new ArrayList<CreatureListener>();
		currentPosition = startPosition.clone();
		this.currentDirection = currentDirection;
		dead = false;
		this.points = points;
	}

	public final void setDirectionToPosition(
			final HashMap<Direction, Position> directionToPosition) {
		this.directionToPosition = directionToPosition;
	}

	public final HashMap<Direction, Position> getDirectionToPosition() {
		return directionToPosition;
	}

	public final Position getRelativePosition(final Direction direction) {
		return directionToPosition.get(direction);
	}

	public final void setCurrentPosition(final Position currentPosition) {
		this.currentPosition = currentPosition;
		fireCreatureChangedPosition();
	}

	public final Position getCurrentPosition() {
		return currentPosition;
	}

	public final void setDead() {
		dead = true;
		fireCreatureSetDead();
	}

	public final void setAlive() {
		dead = false;
		fireCreatureSetAlive();
	}

	public final boolean isDead() {
		return dead;
	}

	public final void setCurrentDirection(final Direction currentDirection) {
		this.currentDirection = currentDirection;
		fireCreatureChangedDirection();
	}

	public final Direction getCurrentDirection() {
		return currentDirection;
	}

	public final Position getStartPosition() {
		return startPosition;
	}

	public final int getPoints() {
		return points;
	}

	public final void setVulnerable() {
		vulnerable = true;
		fireCreatureSetVulnerable();
	}

	public final void setInvulnerable() {
		vulnerable = false;
		fireCreatureSetInvulnerable();
	}

	public final boolean isVulnerable() {
		return vulnerable;
	}

	public final void setCurrentItems(final ArrayList<Item> currentItems) {
		this.currentItems = currentItems;
	}

	public final ArrayList<Item> getCurrentItems() {
		return currentItems;
	}

	public final void setCreaturesOnCurrentFloor(
			final ArrayList<Creature> creaturesOnCurrentFloor) {
		this.creaturesOnCurrentFloor = creaturesOnCurrentFloor;
	}

	public final ArrayList<Creature> getCreaturesOnCurrentFloor() {
		return creaturesOnCurrentFloor;
	}

	protected final void resetTimer() {
		timer = 0;
	}

	public final void setTimer(int time) {
		timer = time;
	}

	protected final void decrementTimer() {
		int runningOut = 10;
		
		if (timer > 0) {
			timer--;
			if (timer < runningOut) {
				fireCreatureTimerIsRunningOut();
			}
		} else {
			setVulnerable();
			fireCreatureTimerTimedOut();
		}
	}

	public final String getName() {
		return name;
	}

	public final void setName(final String name) {
		this.name = name;
	}

	public final boolean currentCreaturesContainsAiControlledCreature() {
		for (final Creature creature : creaturesOnCurrentFloor) {
			if (!creature.isControlledByPlayer()) {
				return true;
			}
		}

		return false;
	}

	public final boolean currentAiControlledCreaturesAreVulnerable() {
		for (final Creature creature : creaturesOnCurrentFloor) {
			if (!creature.isControlledByPlayer() && !creature.isDead()
					&& !creature.isVulnerable()) {
				return false;
			}
		}

		return true;
	}

	public final void addCreatureListener(final CreatureListener listener) {
		listeners.add(listener);
	}

	public final void removeCreatureListener(final CreatureListener listener) {
		listeners.remove(listener);
	}

	private final void fireCreatureChangedPosition() {
		for (final CreatureListener listener : listeners) {
			listener.creatureChangedPosition(this);
		}
	}

	private final void fireCreatureChangedDirection() {
		for (final CreatureListener listener : listeners) {
			listener.creatureChangedDirection(this);
		}
	}

	private final void fireCreatureSetDead() {
		for (final CreatureListener listener : listeners) {
			listener.creatureSetDead(this);
		}
	}

	private final void fireCreatureSetAlive() {
		for (final CreatureListener listener : listeners) {
			listener.creatureSetAlive(this);
		}
	}

	private final void fireCreatureSetVulnerable() {
		for (final CreatureListener listener : listeners) {
			listener.creatureSetVulnerable(this);
		}
	}

	private final void fireCreatureSetInvulnerable() {
		for (final CreatureListener listener : listeners) {
			listener.creatureSetInvulnerable(this);
		}
	}

	private final void fireCreatureTimerIsRunningOut() {
		for (final CreatureListener listener : listeners) {
			listener.creatureTimerIsRunningOut(this);
		}
	}
	
	private final void fireCreatureTimerTimedOut() {
		for (final CreatureListener listener : listeners) {
			listener.creatureTimerTimedOut(this);
		}
	}

	abstract public boolean isControlledByPlayer();

	abstract public Position getNextPosition();

	abstract public void reset();

	abstract public String toString();
}
